package server;

import java.io.Closeable;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Set;

public class ChatServer {

    private static final int DEFAULT_PORT = 8888;
    private static final String QUIT = "quit";
    private static final int BUFFER = 1024;


    private ServerSocketChannel server;
    private Selector selector;
    private ByteBuffer rBuffer = ByteBuffer.allocate(BUFFER);
    private ByteBuffer wBuffer = ByteBuffer.allocate(BUFFER);
    private Charset charset = Charset.forName("UTF-8");
    private int port;

    public ChatServer() {
        this(DEFAULT_PORT);
    }

    public ChatServer(int port) {
        this.port = port;
    }

    private void start() {
        try {
            //创建通道
            server = ServerSocketChannel.open();
            //默认阻塞，改成非阻塞
            server.configureBlocking(false);
            server.socket().bind(new InetSocketAddress(port));

            selector =Selector.open();
            //注册监听连接事件
            server.register(selector,SelectionKey.OP_ACCEPT);
            System.out.println("启动服务器,监听端口:["+port+"...");

            while (true){

                //select函数本身阻塞式
                selector.select();
                Set<SelectionKey> selectionKeys=selector.selectedKeys();

                for (SelectionKey key:selectionKeys) {
                    //处理被处罚事件
                    handles(key);
                }
                //清楚过期事件
                selectionKeys.clear();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            close(selector);
        }

    }

    private void handles(SelectionKey key) throws IOException {
        // ACCEPT事件 - 和客户端建立了连接
        if(key.isAcceptable()){
            //得到该事件的通道
            SocketChannel client=(SocketChannel)key.channel();
            client.configureBlocking(false);
            client.register(selector,SelectionKey.OP_READ);
            System.out.println(getClientName(client));
        }
        // READ事件 - 客户端发送了消息
        else if(key.isReadable()){
            SocketChannel client=(SocketChannel)key.channel();

            //读出消息
            String fwdMsg= receive(client);
            if(fwdMsg.isEmpty()){
                //客户端异常
                key.cancel();
                selector.wakeup();
            }else{
                //转发消息
                forwardMessage(client,fwdMsg);

                //检查用户是否退出
                if (readyToQuit(fwdMsg)){
                    key.cancel();
                    selector.wakeup();
                    System.out.println(getClientName(client));
                }
            }

        }



    }

    private void forwardMessage(SocketChannel client, String fwdMsg) throws IOException {
        for (SelectionKey key: selector.keys()) {

            Channel connectedClient = key.channel();
            if (connectedClient instanceof ServerSocketChannel) {
                continue;
            }
            if(key.isValid() && !client.equals(connectedClient)){
                wBuffer.clear();
                wBuffer.put(charset.encode(getClientName(client)+":"+fwdMsg));
                wBuffer.flip();
                while (wBuffer.hasRemaining()){
                    ((SocketChannel)connectedClient).write(wBuffer);
                }
            }
        }
    }

    private String receive(SocketChannel client) throws IOException {
        rBuffer.clear();
        while (client.read(rBuffer)>0) {

        }
        //写模式转换为读模式
        rBuffer.flip();
        return String.valueOf(charset.decode(rBuffer));
    }

    private String getClientName(SocketChannel client) {
        return "客户端[" + client.socket().getPort() + "]";
    }

    private boolean readyToQuit(String msg) {
        return QUIT.equals(msg);
    }

    private void close(Closeable closable) {
        if (closable != null) {
            try {
                closable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        ChatServer chatServer = new ChatServer(7777);
        chatServer.start();
    }
}
